package com.yunmasoft.service.pay.wexin;

import com.alibaba.fastjson.JSONObject;
import com.yunmasoft.service.pay.wexin.exception.WxmpException;
import com.house.ujiayigou.thirdpart.weixin.JSONMaterializer;
import com.house.ujiayigou.thirdpart.weixin.URLBuilder;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;

import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Properties;

//import org.apache.http.client.*;

/**
 * @author galin<cuter44   @   foxmail.com>
 * @date 2014/12/25
 */
public abstract class WxmpRequestBase {
    // SSL
    /**
     * Default http client to use to send request to weixin server.
     * Provide class-scope http client, which is used when <code>httpClient</code> is null, major for single-account use.
     * You can tweak this with your own. This will takes effect on follow-up request whose <code>httpClient</code> is unset.
     */
    public static CloseableHttpClient defaultHttpClient;

    /**
     * Http client to use to send request to weixin server.
     * Provide object-scope http client, major for multi-account use.
     * You can directly set this field. This will takes effect on time when <code>.execute()</code> is called.
     */
    public CloseableHttpClient httpClient;
    // CONFIG
    protected Properties conf;

    // CONSTRUCT
    public WxmpRequestBase(Properties aConf) {
        this.conf = aConf;

        return;
    }

    protected static CloseableHttpClient buildHttpClient(SSLContext ctx) {
        HttpClientBuilder hcb = HttpClientBuilder.create()
                .disableAuthCaching()
                .disableCookieManagement()
                .setSslcontext(ctx);

        return (hcb.build());
    }

    /**
     * Config defualt http client
     * The existing <code>defaultHttpClient</code> will be dropped, without closing.
     */
    public static void configDefaultHC(SSLContext ctx) {
        defaultHttpClient = buildHttpClient(ctx);
    }

    protected static JSONObject buildJSONBody(JSONObject schema, Properties p) {
        return (
                JSONMaterializer.instance.materialize(schema, p)
        );
    }

    /**
     * Config http client
     * The existing <code>httpClient</code> will be dropped, without closing.
     *
     * @return this
     */
    public WxmpRequestBase configHC(SSLContext ctx) {
        this.httpClient = buildHttpClient(ctx);

        return (this);
    }

    public String getProperty(String key) {
        return (
                this.conf.getProperty(key)
        );
    }

    /**
     * chain supported
     */
    public WxmpRequestBase setProperty(String key, String value) {
        this.conf.setProperty(key, value);
        return (this);
    }

    /**
     * batch setProperty
     *
     * @param aConf a Map contains key-value pairs, where key must be String, and values must implement toString() at least.
     */
    public WxmpRequestBase setProperties(Map aConf) {
        this.conf.putAll(aConf);
        return (this);
    }

    // TO_URL

    // BUILD
    public abstract WxmpRequestBase build();

    /**
     * Extract URL to execute request on client
     */
    public abstract String toURL()
            throws UnsupportedOperationException;

    /**
     * Provide query string to sign().
     * toURL() may not invoke this method.
     */
    protected String toQueryString(List<String> paramNames) {
        URLBuilder ub = new URLBuilder();

        for (String key : paramNames)
            ub.appendParam(key, this.getProperty(key));

        return (ub.toString());
    }

    // EXECUTE

    /**
     * Execute the constructed query
     */
    public abstract WxmpResponseBase execute()
            throws IOException, WxmpException, UnsupportedOperationException;

    /**
     */
    public String executeGet(String fullURL)
            throws IOException {
        HttpGet req = new HttpGet(fullURL);

        CloseableHttpResponse resp = this.getHttpClient().execute(req);

        String content = WxpayRequestBase.getResponseBody(resp);

        resp.close();

        return (content);
    }

    public String executePostJSON(String fullURL, String bodyJSON)
            throws IOException {
        HttpPost req = new HttpPost(fullURL);

        // DEBUGING set proxy if need to capture the traffic
        // remember to load your proxy certificate
        //RequestConfig rc = RequestConfig.custom()
        //.setProxy(
        //new HttpHost("10.50.9.21", 8888)
        //)
        //.build();
        //req.setConfig(rc);

        req.setEntity(
                new StringEntity(
                        bodyJSON,
                        ContentType.create("application/json", "utf-8")
                )
        );

        CloseableHttpResponse resp = this.getHttpClient().execute(req);

        String content = WxpayRequestBase.getResponseBody(resp);

        resp.close();

        return (content);
    }

    public String executePostJSON(String fullURL, JSONObject bodyJSON)
            throws IOException {
        return (
                this.executePostJSON(fullURL, bodyJSON.toString())
        );
    }

    /**
     * @return a http client, ought to be used to process the request.
     */
    protected CloseableHttpClient getHttpClient() {
        return (
                (this.httpClient != null) ? this.httpClient : defaultHttpClient
        );
    }


    // MISC
}

